iT邦幫忙

2025 iThome 鐵人賽

DAY 12
2

前面寫了這麼多篇文章,都沒提到關於樣式的設定。
今天我們終於要進入樣式了,Web Component 的樣式其實是我在學習過程中時常卡關的部分。
因為樣式隔離的關係,外部 CSS 沒辦法直接改變 Shadow DOM 裡面元素的 style,這也可以說是 Web Component 一直的一個缺點吧。

套用樣式 的過程中,可能會體會到 Shadow DOM 的雙面刃。
Shadow DOM 在某些情境下是好事,因為保證元件樣式不會被外部影響,但在另一面卻可能造成麻煩。
當你的網站有主題顏色(theme colors)或是想統一字型、按鈕樣式時,是無法直接在外部修改的,所以我們會需要用到其他方法,讓外部套用者可以修改在 Shadow DOM 內元件的樣式。

做個測試吧!


我們先建立一個簡單的 Shadow DOM 元件,試試看在外部覆蓋元件的樣式:
card.js

class UNCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>
        .card {
          width: 100px;
          height: 20px;
          padding: 12px;
          border: 2px solid #888888;
          border-radius: 8px;
          font-size: 14px;
          font-weight: 600;
        }
      </style>
      
      <div class='card'>請幫我加入樣式</div>
    `;
  }
}

customElements.define('un-card', UNCard);

index.html

<style>
  un-card .card {
    background-color: #222222;
  }
</style>
<body>
  <un-card></un-card>
</body>

當外部針對卡片元件改變背景樣式時,會發現元件的背景色並未改變。

style

如何在外部改變 Shadow DOM 中元件的樣式?


在前面的文章,其實已經可以透過學到的 attribute 或是 property 來改變元件的樣式。
像是在前面的示範中,我們透過監聽 color 的 attribute 來改變 cat-spinner 中貓咪的顏色。

而今天我們會使用 css 來控制元件樣式,以下有三種方式,可以讓外部改變 Shadow DOM 中的元件樣式。

:host

透過在 Web Component 的樣式中定義 :host 來給予基本元件的樣式。
並且可以再針對特定樣式給予特定 host,像是 :host([primary]),這樣子,外部就可以在標籤上面加入我們已經定義好的一些樣式。
這種做法比較像是元件已經寫好了幾種樣式讓使用者切換,可能是主題色像是 primarysecondary...。

  • 優點:使用者只需要加屬性就能切換樣式。
  • 缺點:只能使用元件作者預先定義好的幾種樣式,無法完全自由修改顏色、字型等。

host

card.js

class UNCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>     
        :host {
          display: block;
          width: 100px;
          height: 20px;
          padding: 12px;
          border: 2px solid #888888;
          border-radius: 8px;
          font-size: 14px;
          font-weight: 600;
        }

        :host([primary]) {
          border-color: #ad43d3;
        }

        :host([rounded-xl]) {
          border-radius: 20px;
        }
      </style>

      <div class='card'>請幫我加入樣式</div>
    `;
  }
}

customElements.define('un-card', UNCard);

index.html

<body style="display: flex; gap: 8px">
  <un-card></un-card>
  <un-card primary></un-card>
  <un-card primary rounded-xl></un-card>
</body>

host

CSS variable

CSS 變數可以突破 Shadow DOM 的隔離限制,外部 CSS 不能直接進 Shadow DOM 修改樣式,但是 CSS 變數卻可以穿透 Shadow DOM ,讓外部來控制內部樣式。
外部可以更靈活的替換 Shadow DOM 元件內的樣式,若有主題色的話也可以更快速的更換。

css variable

card.js

class UNCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>     
        :host {
          display: block;
          width: 100px;
          height: 20px;
          padding: 12px;
          border: 2px solid var(--card-border-color, #333333);
          border-radius: var(--card-radius, 8px);
          color: var(--card-text-color, #888888);
          font-size: 14px;
          font-weight: 600;
        }
      </style>

      <div class='card'>請幫我加入樣式</div>
    `;
  }
}

customElements.define('un-card', UNCard);

index.html

<style>
  un-card {
    --card-border-color: #a3ddfa;
    --card-radius: 8px;
    --card-text-color: #dffabc;
  }
</style>

<body style="display: flex; gap: 8px">
  <un-card></un-card>
</body>

css variable

::part

part 跟前面所說到的 host 其實蠻相似的,但是 part 讓使用者可以彈性調整樣式的幅度比 host 還要更大。

  • 只開放有標記 part 的元素,定義外部可以修改哪些元素的樣式,哪些不行,以達到有限度的開放。
  • 內部可以同時定義多個 part,讓外部可以針對不同 part 進行調整。

part

card.js

class UNCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.innerHTML = `
      <style>     
        .card {
          padding: 8px;
          border: 1px solid #33e4fd;
        }
      </style>
      
      <div part="card" class="card">請幫我加入樣式</div>
    `;
   }
 }

customElements.define('un-card', UNCard);

index.html

<style>
  un-card::part(card) {
    border: 1px dashed #aad332;
    padding: 12px;
    border-radius: 4px;
  }
</style>

<body style="display: flex; gap: 8px">
  <un-card></un-card>
</body>

part

以上三種方法,就是可以改變 Web Component 樣式的方法。
而要如何使用,就看設計元件的你,想要讓外部使用者有多大的彈性囉!


上一篇
Day 11: Web Component 應用-Modal 與 slot
下一篇
Day 13: Web Component 的插槽樣式 ::slotted
系列文
原生元件養成計畫:Web Component13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言